package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.druid.sql.visitor.functions.Char;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.AlertNotifyDto;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.service.AlertRuleService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.AlertDataVo;
import com.zzyl.vo.AlertRuleVo;
import com.zzyl.vo.DeviceDataVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import com.zzyl.websocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class AlertRuleServiceImpl implements AlertRuleService {

    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Value("${zzyl.alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;

    @Value("${zzyl.alert.managerRole}")
    private String managerRole;

    @Autowired
    private WebSocketServer socketServer;

    @Override
    public void create(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.insert(alertRule);
    }

    @Override
    public PageResponse<AlertRuleVo> getPage(Integer pageNum, Integer pageSize, String alertRuleName, String functionName, String productKey) {
        PageHelper.startPage(pageNum,pageSize);
        Page<AlertRuleVo> page = alertRuleMapper.getPage(alertRuleName,functionName,productKey);
        page.getResult().forEach(alertRuleVo -> {
            String rule = CharSequenceUtil.format("{}{}{},持续{}个周期就报警",
                   alertRuleVo.getFunctionName(),alertRuleVo.getOperator(),
                    alertRuleVo.getValue(),alertRuleVo.getDuration());
            alertRuleVo.setRules(rule);
        });
        return PageResponse.of(page,AlertRuleVo.class);
    }

    @Override
    public void updateAlertRule(AlertRuleDto alertRuleDto) {
        AlertRule alertRule = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertRuleMapper.updateAlertRule(alertRule);
    }

    @Override
    public void deleteAlertRule(Long id) {
        alertRuleMapper.deleteAlertRule(id);
    }

    @Override
    public List<AlertRuleVo> getAlertRule(String iotId, String functionId, String productKey) {
        List<AlertRuleVo> list = alertRuleMapper.getAlertRule(iotId,functionId,productKey);
        return list;
    }

    @Override
    public void alertRuleHandler(DeviceDataVo deviceDataVo) {
        //上报时间，不在当前定时任务的一分钟内，数据没有意义
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        if (between > 60){
            return;
        }
        //查询所有设备数据
        List<AlertRuleVo> alertRuleAll = alertRuleMapper.getAlertRule("-1", deviceDataVo.getFunctionId(), deviceDataVo.getProductKey());
        //
        //查询所有设备数据
        List<AlertRuleVo> alertRule = alertRuleMapper.getAlertRule(deviceDataVo.getIotId(),deviceDataVo.getFunctionId(),deviceDataVo.getProductKey());
        Collection<AlertRuleVo> alertRuleVoCollection = CollUtil.addAll(alertRuleAll,alertRule);
        if (ObjectUtil.isEmpty(alertRuleVoCollection)){
            return;
        }
        //数据去匹配多个规则
        alertRuleVoCollection.forEach(alertRuleVo -> {
            alarmDeviceDataHandler(alertRuleVo,deviceDataVo);
        });

    }

    //规则和数据进行处理
    private void alarmDeviceDataHandler(AlertRuleVo alertRuleVo, DeviceDataVo deviceDataVo) {
        //第一层判断时间是否在生效范围
        LocalTime alarmTime = LocalDateTimeUtil.of(deviceDataVo.getAlarmTime()).toLocalTime(); //07:00:00
        String[] split = alertRuleVo.getAlertEffectivePeriod().split("~");
        LocalTime start = LocalTime.parse(split[0]); //08:00:00
        LocalTime end = LocalTime.parse(split[1]); //15:00:00
        if (alarmTime.isBefore(start) || alarmTime.isAfter(end)){
            return;
        }

        //第二层校验
        //校验数据是否达到阈值
        Float dataValue = Float.valueOf(deviceDataVo.getDataValue()); //数据的值
        Float ruleValue = alertRuleVo.getValue(); //阈值
        //x>y ---> 1 x=y --->0 x<y --->-1
        int compare = NumberUtil.compare(dataValue,ruleValue);
        String dataKey = deviceDataVo.getProductKey() +":"+ deviceDataVo.getIotId() +":"+deviceDataVo.getFunctionId();

        if ((compare>0 && alertRuleVo.getOperator().equals(">=")) || (compare<0 && alertRuleVo.getOperator().equals("<"))){
            log.debug("此时设备上报数据达到阈值");
        }else {
            redisTemplate.delete(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey);
            return;
        }

        //第三层校验
        String silent = redisTemplate.opsForValue().get(CacheConstants.ALERT_SILENT_PREFIX +dataKey);
        if (ObjectUtil.isNotEmpty(silent)){
            return;
        }

        //第四层判断
        String s = redisTemplate.opsForValue().get(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey);

        int count = ObjectUtil.isEmpty(s) ? 1 : Integer.parseInt(s)+1;

        if (count != alertRuleVo.getDuration()){
            redisTemplate.opsForValue().set(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey,count + "");
            return;
        }

        //都成功要删除redis报警次数
        redisTemplate.delete(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey);

        //添加沉默周期
        redisTemplate.opsForValue().set(CacheConstants.ALERT_SILENT_PREFIX + dataKey,"沉默周期的值",alertRuleVo.getAlertSilentPeriod(), TimeUnit.MINUTES);

        //插入告警数据
        //设备数据是老人数据，还是固定数据
        List<Long> userIds = null;
        if (alertRuleVo.getAlertDataType().equals(0)){ //老人护理员
            if (deviceDataVo.getLocationType().equals(0)){ //老人的随身设备
                userIds = alertRuleMapper.queryDeviceElder(deviceDataVo.getIotId());
            }else { //老人的床固定设备
                userIds = alertRuleMapper.queryDeviceElderAndBed(deviceDataVo.getIotId());
            }
        }else { //维修员
            userIds = alertRuleMapper.queryDeviceRepair(deviceMaintainerRole);
        }
        //超级管理员都要执行
        List<Long> managers = alertRuleMapper.queryDeviceRepair(managerRole);
        Collection<Long> userList = CollUtil.addAll(userIds, managers);
        //批量插入数据库
        List<AlertData> list = new ArrayList<>();
        userList.forEach(userId -> {
            AlertData alertData = new AlertData();
            BeanUtil.copyProperties(deviceDataVo,alertData);
            alertData.setUserId(userId);
            String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警",alertRuleVo.getFunctionName(),
                    alertRuleVo.getOperator(), alertRuleVo.getValue(), alertRuleVo.getDuration());
            alertData.setAlertRuleId(alertRuleVo.getId());
            alertData.setAlertReason(alertReason);
            alertData.setType(alertRuleVo.getAlertDataType());
            alertData.setStatus(0);
            list.add(alertData);
        });

        alertRuleMapper.batchInsert(list);
        //websocket的消息告警推送
        AlertNotifyDto alertNotifyDto = new AlertNotifyDto();
        AlertData alertData = list.get(0);
        BeanUtil.copyProperties(alertData, alertNotifyDto);
        alertNotifyDto.setFunctionName(alertRuleVo.getFunctionName());
        alertNotifyDto.setAlertDataType(alertData.getType());
        alertNotifyDto.setNotifyType(1);
        socketServer.sendMessageToConsumer(alertNotifyDto,(List<Long>) userList);
    }
}


















